// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef __MESOS_V1_HPP__
#define __MESOS_V1_HPP__

#include <iosfwd>
#include <string>
#include <vector>

#include <boost/functional/hash.hpp>

#include <google/protobuf/repeated_field.h>

#include <mesos/v1/mesos.pb.h> // ONLY USEFUL AFTER RUNNING PROTOC.

#include <stout/strings.hpp>

// This file includes definitions for operators on public protobuf
// classes (defined in mesos.proto, module.proto, etc.) that don't
// have these operators generated by the protobuf compiler. The
// corresponding definitions are in src/v1/type_utils.cpp.
//
// Mesos modules need some of the protobuf classes defined in
// mesos.proto, module.proto, etc., and require some of these
// operators declared in mesos.hpp. Exposing mesos.hpp
// allows us to build modules without having a dependency on mesos
// source tree (src/*).

namespace mesos {
namespace v1 {

bool operator==(const CommandInfo& left, const CommandInfo& right);
bool operator==(const CommandInfo::URI& left, const CommandInfo::URI& right);
bool operator==(const Credential& left, const Credential& right);
bool operator==(const DiscoveryInfo& left, const DiscoveryInfo& right);
bool operator==(const Environment& left, const Environment& right);
bool operator==(const ExecutorInfo& left, const ExecutorInfo& right);
bool operator==(const FileInfo& left, const FileInfo& right);
bool operator==(const Label& left, const Label& right);
bool operator==(const Labels& left, const Labels& right);
bool operator==(const MasterInfo& left, const MasterInfo& right);

bool operator==(
    const ResourceStatistics& left,
    const ResourceStatistics& right);

bool operator==(const AgentInfo& left, const AgentInfo& right);
bool operator==(const TaskStatus& left, const TaskStatus& right);
bool operator==(const URL& left, const URL& right);
bool operator==(const Volume& left, const Volume& right);

bool operator!=(const Labels& left, const Labels& right);
bool operator!=(const TaskStatus& left, const TaskStatus& right);


inline bool operator==(const ContainerID& left, const ContainerID& right)
{
  return left.value() == right.value();
}


inline bool operator==(const ExecutorID& left, const ExecutorID& right)
{
  return left.value() == right.value();
}


inline bool operator==(const FrameworkID& left, const FrameworkID& right)
{
  return left.value() == right.value();
}


inline bool operator==(const FrameworkInfo& left, const FrameworkInfo& right)
{
  return (left.name() == right.name()) && (left.user() == right.user());
}


inline bool operator==(const OfferID& left, const OfferID& right)
{
  return left.value() == right.value();
}


inline bool operator==(const AgentID& left, const AgentID& right)
{
  return left.value() == right.value();
}


inline bool operator==(const TaskID& left, const TaskID& right)
{
  return left.value() == right.value();
}


inline bool operator==(const TimeInfo& left, const TimeInfo& right)
{
  return left.nanoseconds() == right.nanoseconds();
}


inline bool operator==(const DurationInfo& left, const DurationInfo& right)
{
  return left.nanoseconds() == right.nanoseconds();
}


inline bool operator==(const ContainerID& left, const std::string& right)
{
  return left.value() == right;
}


inline bool operator==(const ExecutorID& left, const std::string& right)
{
  return left.value() == right;
}


inline bool operator==(const FrameworkID& left, const std::string& right)
{
  return left.value() == right;
}


inline bool operator==(const OfferID& left, const std::string& right)
{
  return left.value() == right;
}


inline bool operator==(const AgentID& left, const std::string& right)
{
  return left.value() == right;
}


inline bool operator==(const TaskID& left, const std::string& right)
{
  return left.value() == right;
}


/**
 * For machines to match, both the `hostname` and `ip` must be equivalent.
 * Hostname is not case sensitive, so it is lowercased before comparison.
 */
inline bool operator==(const MachineID& left, const MachineID& right)
{
  // NOTE: Both fields default to the empty string if they are not specified,
  // so the string comparisons are safe.
  return left.has_hostname() == right.has_hostname() &&
    strings::lower(left.hostname()) == strings::lower(right.hostname()) &&
    left.has_ip() == right.has_ip() &&
    left.ip() == right.ip();
}


inline bool operator!=(const ContainerID& left, const ContainerID& right)
{
  return left.value() != right.value();
}


inline bool operator!=(const ExecutorID& left, const ExecutorID& right)
{
  return left.value() != right.value();
}


inline bool operator!=(const FrameworkID& left, const FrameworkID& right)
{
  return left.value() != right.value();
}


inline bool operator!=(const AgentID& left, const AgentID& right)
{
  return left.value() != right.value();
}


inline bool operator!=(const TimeInfo& left, const TimeInfo& right)
{
  return !(left == right);
}


inline bool operator!=(const DurationInfo& left, const DurationInfo& right)
{
  return !(left == right);
}


inline bool operator<(const ContainerID& left, const ContainerID& right)
{
  return left.value() < right.value();
}


inline bool operator<(const ExecutorID& left, const ExecutorID& right)
{
  return left.value() < right.value();
}


inline bool operator<(const FrameworkID& left, const FrameworkID& right)
{
  return left.value() < right.value();
}


inline bool operator<(const OfferID& left, const OfferID& right)
{
  return left.value() < right.value();
}


inline bool operator<(const AgentID& left, const AgentID& right)
{
  return left.value() < right.value();
}


inline bool operator<(const TaskID& left, const TaskID& right)
{
  return left.value() < right.value();
}


std::ostream& operator<<(
    std::ostream& stream,
    const CapabilityInfo& capabilityInfo);


std::ostream& operator<<(std::ostream& stream, const ContainerID& containerId);


std::ostream& operator<<(
    std::ostream& stream,
    const ContainerInfo& containerInfo);


std::ostream& operator<<(std::ostream& stream, const ExecutorID& executorId);


std::ostream& operator<<(std::ostream& stream, const ExecutorInfo& executor);


std::ostream& operator<<(std::ostream& stream, const FrameworkID& frameworkId);


std::ostream& operator<<(std::ostream& stream, const MasterInfo& master);


std::ostream& operator<<(std::ostream& stream, const OfferID& offerId);


std::ostream& operator<<(std::ostream& stream, const RateLimits& limits);


std::ostream& operator<<(std::ostream& stream, const RLimitInfo& limits);


std::ostream& operator<<(std::ostream& stream, const AgentID& agentId);


std::ostream& operator<<(std::ostream& stream, const AgentInfo& agent);


std::ostream& operator<<(std::ostream& stream, const TaskID& taskId);


std::ostream& operator<<(std::ostream& stream, const MachineID& machineId);


std::ostream& operator<<(std::ostream& stream, const TaskInfo& task);


std::ostream& operator<<(
    std::ostream& stream,
    const TaskGroupInfo& taskGroupInfo);


std::ostream& operator<<(std::ostream& stream, const TaskState& state);


std::ostream& operator<<(
    std::ostream& stream,
    const std::vector<TaskID>& taskIds);


std::ostream& operator<<(
    std::ostream& stream,
    const FrameworkInfo::Capability& capability);


std::ostream& operator<<(std::ostream& stream, const Image::Type& imageType);


template <typename T>
inline std::ostream& operator<<(
    std::ostream& stream,
    const google::protobuf::RepeatedPtrField<T>& messages)
{
  stream << "[ ";
  for (auto it = messages.begin(); it != messages.end(); ++it) {
    if (it != messages.begin()) {
      stream << ", ";
    }
    stream << *it;
  }
  stream << " ]";
  return stream;
}


std::ostream& operator<<(
    std::ostream& stream,
    const hashmap<std::string, std::string>& map);

} // namespace v1 {
} // namespace mesos {

namespace std {

template <>
struct hash<mesos::v1::CommandInfo::URI>
{
  typedef size_t result_type;

  typedef mesos::v1::CommandInfo::URI argument_type;

  result_type operator()(const argument_type& uri) const
  {
    size_t seed = 0;

    if (uri.extract()) {
      seed += 11;
    }

    if (uri.executable()) {
      seed += 2003;
    }

    boost::hash_combine(seed, uri.value());
    return seed;
  }
};


template <>
struct hash<mesos::v1::ContainerID>
{
  typedef size_t result_type;

  typedef mesos::v1::ContainerID argument_type;

  result_type operator()(const argument_type& containerId) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, containerId.value());
    return seed;
  }
};


template <>
struct hash<mesos::v1::ExecutorID>
{
  typedef size_t result_type;

  typedef mesos::v1::ExecutorID argument_type;

  result_type operator()(const argument_type& executorId) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, executorId.value());
    return seed;
  }
};


template <>
struct hash<mesos::v1::FrameworkID>
{
  typedef size_t result_type;

  typedef mesos::v1::FrameworkID argument_type;

  result_type operator()(const argument_type& frameworkId) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, frameworkId.value());
    return seed;
  }
};


template <>
struct hash<mesos::v1::OfferID>
{
  typedef size_t result_type;

  typedef mesos::v1::OfferID argument_type;

  result_type operator()(const argument_type& offerId) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, offerId.value());
    return seed;
  }
};


template <>
struct hash<mesos::v1::AgentID>
{
  typedef size_t result_type;

  typedef mesos::v1::AgentID argument_type;

  result_type operator()(const argument_type& agentId) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, agentId.value());
    return seed;
  }
};


template <>
struct hash<mesos::v1::TaskID>
{
  typedef size_t result_type;

  typedef mesos::v1::TaskID argument_type;

  result_type operator()(const argument_type& taskId) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, taskId.value());
    return seed;
  }
};


template <>
struct hash<mesos::v1::TaskState>
{
  typedef size_t result_type;

  typedef mesos::v1::TaskState argument_type;

  result_type operator()(const argument_type& taskState) const
  {
    // Use the underlying type of the enum as hash value.
    return static_cast<size_t>(taskState);
  }
};


template <>
struct hash<mesos::v1::TaskStatus_Source>
{
  typedef size_t result_type;

  typedef mesos::v1::TaskStatus_Source argument_type;

  result_type operator()(const argument_type& source) const
  {
    // Use the underlying type of the enum as hash value.
    return static_cast<size_t>(source);
  }
};


template <>
struct hash<mesos::v1::TaskStatus_Reason>
{
  typedef size_t result_type;

  typedef mesos::v1::TaskStatus_Reason argument_type;

  result_type operator()(const argument_type& reason) const
  {
    // Use the underlying type of the enum as hash value.
    return static_cast<size_t>(reason);
  }
};


template <>
struct hash<mesos::v1::Image::Type>
{
  typedef size_t result_type;

  typedef mesos::v1::Image::Type argument_type;

  result_type operator()(const argument_type& imageType) const
  {
    // Use the underlying type of the enum as hash value.
    return static_cast<size_t>(imageType);
  }
};


template <>
struct hash<std::pair<mesos::v1::FrameworkID, mesos::v1::ExecutorID>>
{
  typedef size_t result_type;

  typedef std::pair<
      mesos::v1::FrameworkID, mesos::v1::ExecutorID> argument_type;

  result_type operator()(const argument_type& pair) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, std::hash<mesos::v1::FrameworkID>()(pair.first));
    boost::hash_combine(seed, std::hash<mesos::v1::ExecutorID>()(pair.second));
    return seed;
  }
};


template <>
struct hash<mesos::v1::MachineID>
{
  typedef size_t result_type;

  typedef mesos::v1::MachineID argument_type;

  result_type operator()(const argument_type& machineId) const
  {
    size_t seed = 0;
    boost::hash_combine(seed, strings::lower(machineId.hostname()));
    boost::hash_combine(seed, machineId.ip());
    return seed;
  }
};

} // namespace std {

#endif // __MESOS_V1_HPP__
